/*
echo server with poll.
**/

#include <define.h>
#include <error.h>
#include <wrapfunc.h>
#include <sockfunc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <poll.h>
#include <errno.h>
#include <string.h>


#define OPEN_MAX 3000


int
main() {
	struct sockaddr_in local_addr;
	memset(&local_addr, 0, sizeof(local_addr));
	local_addr.sin_family = AF_INET;
	local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	local_addr.sin_port = htons(SERV_PORT);

	int listenfd = Socket(AF_INET, SOCK_STREAM, 0);
	Bind(listenfd, (struct sockaddr*)&local_addr, sizeof(local_addr));
	Listen(listenfd, LISTENQ);
	error_msg("wait for clients");

	struct pollfd conn_fds[OPEN_MAX];
	int connindex, max_connindex, nready;
	struct sockaddr_in connaddr;
	socklen_t len;
	int connfd;
	char buf[MAX_LINE];
	ssize_t n;

	max_connindex = -1;
	for (int i = 0; i < OPEN_MAX; i++)
		conn_fds[i].fd = -1;

	conn_fds[0].fd = listenfd;
	conn_fds[0].events = POLLIN;
	max_connindex = 0;

	for (;;) {
		nready = Poll(conn_fds, max_connindex+1, -1);
		if (conn_fds[0].revents & POLLIN) {
			len = sizeof(connaddr);
			connfd = Accept(listenfd, (struct sockaddr*)&connaddr, &len);
			error_msg("new connection fd:%d %s", connfd, sock_ntop((struct sockaddr*)&connaddr, len));

			connindex = -1;
			for (int i = 1; i < OPEN_MAX; i++) {
				if (conn_fds[i].fd < 0) {
					connindex = i;
					break;
				}
			}

			if (connindex < 0) {
				error_msg("too many connections close fd:%d", connfd);
				Close(connfd);
			} else {
				conn_fds[connindex].fd = connfd;
				conn_fds[connindex].events = POLLIN;
				if (connindex > max_connindex)
					max_connindex = connindex;
			}

			if (--nready <= 0)
				continue;
		}

		for (int i = 1; i <= max_connindex; i++) {
			connfd = conn_fds[i].fd;
			if (connfd < 0)
				continue;
			// POLLERR 不需要在 events 中设置，若出错时会在 revents 中返回，
			// 此后 read 将返回 -1 且 errno 会记录错误值。
			if (conn_fds[i].revents & (POLLIN | POLLERR)) {
				if ((n=read(connfd, buf, sizeof(buf))) <= 0) {
					if (n == 0) {
						error_msg("connection fd:%d is closed", connfd);
					} else {
						if (errno == ECONNRESET)
							error_msg("connection fd:%d is reset", connfd);
						else
							error_dump("fail to read for fd:%d", connfd);
					}
					conn_fds[i].fd = -1;
					Close(connfd);
				} else {
					Write(connfd, buf, n);
				}

				if (--nready <= 0)
					break;
			}
		}
	}

	Close(listenfd);
	return 0;
}
